#include "scope.h"

static int g_scope_trigger_code_offset;

static void scope_tigger_init_gpio(void)
{
    gpio_init(PIN_ADC_TRIG0);
    gpio_init(PIN_ADC_TRIG1);
}

void scope_trigger_init(void)
{
    pio_sm_claim(SCOPE_ADC_PIO, SCOPE_TRIGGER_SM);
    g_scope_trigger_code_offset = pio_add_program(SCOPE_ADC_PIO, &trigger_program);
    g_scope_cfg.trigger_dma_chan = dma_claim_unused_channel(true);
    scope_tigger_init_gpio();
}

static void scope_trigger_set_code(void)
{
    volatile uint32_t *code0 = &SCOPE_ADC_PIO->instr_mem[g_scope_trigger_code_offset + trigger_offset_trigger_pin_cfg0];
    volatile uint32_t *code1 = &SCOPE_ADC_PIO->instr_mem[g_scope_trigger_code_offset + trigger_offset_trigger_pin_cfg1];
    int pin = PIN_ADC_TRIG0;
    bool high = true;
    
    if (g_scope_cfg.trigger_chnl == 0) {
        pin = PIN_ADC_TRIG0;
    } else {
        pin = PIN_ADC_TRIG1;
    }
    if (g_scope_cfg.trigger_edge == SCOPE_EDGE_RISING) {
        high = true;
    }
    if (g_scope_cfg.trigger_edge == SCOPE_EDGE_FALLING) {
        high = false;
    }
    if (g_scope_cfg.trigger_edge == SCOPE_EDGE_BOTH) {
        if (gpio_get(pin) == 0) {
            high = true;
        } else {
            high = false;
        }
    }
    *code0 = pio_encode_wait_gpio(!high, pin);
    *code1 = pio_encode_wait_gpio(high, pin);
}

void scope_trigger_start(void)
{
    int freq = clock_get_hz(clk_sys);
    static uint32_t stop_inst;

    stop_inst = SCOPE_ADC_PIO->ctrl & ~(1u << SCOPE_ADC_CLK_SM);
    pio_sm_set_enabled(SCOPE_ADC_PIO, SCOPE_TRIGGER_SM, false);
    dma_channel_abort(g_scope_cfg.trigger_dma_chan);

    if (g_scope_cfg.trigger_mode == SCOPE_MODE_NORMAL) {
        return;
    }
    pio_sm_config pio_cfg = trigger_program_get_default_config(g_scope_trigger_code_offset);
    sm_config_set_clkdiv(&pio_cfg, freq / 2 / g_scope_cfg.rate);
    sm_config_set_in_shift(&pio_cfg, true, true, 32);
    sm_config_set_out_shift(&pio_cfg, true, true, 32);
    pio_sm_init(SCOPE_ADC_PIO, SCOPE_TRIGGER_SM, g_scope_trigger_code_offset, &pio_cfg);

    dma_channel_config cfg = dma_channel_get_default_config(g_scope_cfg.trigger_dma_chan);
    channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32);
    channel_config_set_read_increment(&cfg, false);
    channel_config_set_write_increment(&cfg, false);
    channel_config_set_dreq(&cfg, SCOPE_TRIGGER_RX_REQ);
    channel_config_set_chain_to(&cfg, g_scope_cfg.trigger_dma_chan);
    // channel_config_set_high_priority(&cfg, true);

    dma_channel_configure(
        g_scope_cfg.trigger_dma_chan,
        &cfg,
        &SCOPE_ADC_PIO->ctrl,
        &stop_inst,
        1,
        true
    );
    scope_trigger_set_code();
    // pio_sm_put(SCOPE_ADC_PIO, SCOPE_TRIGGER_SM, SCOPE_ADC_BUFFER_WORDS * 4 / 2);
    pio_sm_put(SCOPE_ADC_PIO, SCOPE_TRIGGER_SM, psram_size() / sizeof(uint32_t) * 4 / 2);
    pio_sm_set_enabled(SCOPE_ADC_PIO, SCOPE_TRIGGER_SM, true);
}

static void scope_trigger_set_chnl(char *value)
{
    g_scope_cfg.trigger_chnl = atoi(value);
    scope_trigger_start();
    log_printf("TriggerChnl: %d\n", g_scope_cfg.trigger_chnl);
}
HTTPD_CGI_PARAM_DEFINE("TriggerChnl", scope_trigger_set_chnl);

static void scope_set_trigger_edge(char *value)
{
    if (strcmp(value, "Rising") == 0) {
        g_scope_cfg.trigger_edge = SCOPE_EDGE_RISING;
    }
    if (strcmp(value, "Falling") == 0) {
        g_scope_cfg.trigger_edge = SCOPE_EDGE_FALLING;
    }
    if (strcmp(value, "Both") == 0) {
        g_scope_cfg.trigger_edge = SCOPE_EDGE_BOTH;
    }
    scope_trigger_start();
    log_printf("TriggerEdge: %d\n", g_scope_cfg.trigger_edge);
}
HTTPD_CGI_PARAM_DEFINE("TriggerEdge", scope_set_trigger_edge);

static int scope_trigger_get_dac(void)
{
    int scale, dac;

    if (g_scope_cfg.trigger_chnl == 0) {
        scale = g_scope_cfg.ch0_scale;
    } else {
        scale = g_scope_cfg.ch1_scale;
    }
    dac = g_scope_cfg.trigger_level_mv * 1000 / 128 / g_scope_cfg.cali_data.ch[g_scope_cfg.trigger_chnl].scale[scale];
    dac = dac + g_scope_cfg.cali_data.ch[g_scope_cfg.trigger_chnl].gnd_trigger[scale];
    return dac;
}

static void scope_set_trigger_level(char *value)
{
    int dac = 0;

    g_scope_cfg.trigger_level_mv = atoi(value);
    if (g_scope_cfg.trigger_level_mv >= 50000) {
        g_scope_cfg.trigger_level_mv = 50000;
    }
    if (g_scope_cfg.trigger_level_mv <= -50000) {
        g_scope_cfg.trigger_level_mv = -50000;
    }
    dac = scope_trigger_get_dac();
    mcp4725_trigger_set_mv(dac);
    scope_trigger_start();
    log_printf("g_scope_cfg.trigger_level: %d\n", g_scope_cfg.trigger_level_mv);
}
HTTPD_CGI_PARAM_DEFINE("TriggerLevel", scope_set_trigger_level);

void scope_trigger_set_mode(char *value)
{
    if (strcmp(value, "Normal") == 0) {
        g_scope_cfg.trigger_mode = SCOPE_MODE_NORMAL;
    }
    if (strcmp(value, "Single") == 0) {
        g_scope_cfg.trigger_mode = SCOPE_MODE_SINGLE;
    }
    scope_trigger_start();
    log_printf("TriggerMode: %d\n", g_scope_cfg.trigger_mode);
}
HTTPD_CGI_PARAM_DEFINE("TriggerMode", scope_trigger_set_mode);
